package com.gcloud.mesh.asset.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.gcloud.framework.db.PageResult;
import com.gcloud.mesh.asset.dao.CloudResourceConfigDao;
import com.gcloud.mesh.asset.dao.CloudResourceDao;
import com.gcloud.mesh.asset.dao.DatacenterDao;
import com.gcloud.mesh.asset.dao.IaasDao;
import com.gcloud.mesh.asset.entity.CloudResourceEntity;
import com.gcloud.mesh.asset.entity.DatacenterEntity;
import com.gcloud.mesh.asset.entity.IaasEntity;
import com.gcloud.mesh.asset.enums.CloudResourceType;
import com.gcloud.mesh.asset.enums.DeviceType;
import com.gcloud.mesh.asset.enums.MockType;
import com.gcloud.mesh.asset.service.ICloudResourceService;
import com.gcloud.mesh.dcs.dao.AppDao;
import com.gcloud.mesh.dcs.dao.SchedulerImagineDao;
import com.gcloud.mesh.dcs.entity.AppEntity;
import com.gcloud.mesh.framework.core.util.JsonUtil;
import com.gcloud.mesh.header.enums.AuthorityResourceClassification;
import com.gcloud.mesh.header.enums.ResourceSourceType;
import com.gcloud.mesh.header.exception.BaseException;
import com.gcloud.mesh.header.exception.CloudResourceErrorCode;
import com.gcloud.mesh.header.msg.asset.CreateCloudResourceMsg;
import com.gcloud.mesh.header.msg.asset.ImportCloudResourceConfigMsg;
import com.gcloud.mesh.header.msg.asset.PageCloudResourceMsg;
import com.gcloud.mesh.header.msg.asset.UpdateCloudResourceMsg;
import com.gcloud.mesh.header.vo.asset.CloudResourceAnalysisVo;
import com.gcloud.mesh.header.vo.asset.CloudResourceItemVo;
import com.gcloud.mesh.header.vo.asset.MockCloudResourceVo;
import com.gcloud.mesh.header.vo.dcs.AuthorityVo;
import com.gcloud.mesh.monitor.service.StatisticsCheatService;
import com.gcloud.mesh.redis.MockRedis;
import com.gcloud.mesh.sm.service.SmService;
import com.gcloud.mesh.utils.TimestampUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.SysLogUrlOperate;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.exception.ParamException;
import org.jeecg.common.util.LogUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CloudResourceServiceImpl implements ICloudResourceService {

	@Autowired
	private CloudResourceDao cloudResourceDao;
	
	@Autowired
	private CloudResourceConfigDao cloudResourceConfigDao;
	
	@Autowired
	private ImportCloudResourceConfigServiceImpl cloudResourceConfigService;
	@Autowired
	private IaasDao iaasDao;

	@Autowired
	private DatacenterDao datacenterDao;

	@Autowired
	private AppDao appDao;

	
	@Autowired
	private SmService smService;

	@Autowired
	private MockRedis<MockCloudResourceVo> mockRedis;
	
	@Autowired
	private ICloudResourceService cloudResourceService;
	@Autowired
	private SchedulerImagineDao schedulerImagineDao;
	@Autowired
	private StatisticsCheatService statisticsCheatService;
	
	private static final String SM_TOKEN = "sm-token";

	@Override
	public String create(@Valid CreateCloudResourceMsg msg) {
		if (msg.getName() != null) {
			List<CloudResourceEntity> cloudReources = cloudResourceDao.findByProperty("name", msg.getName());
			for (CloudResourceEntity cloudReource : cloudReources) {
				if (cloudReource != null && cloudReource.getName().equals(msg.getName())) {
					throw new ParamException("::资源名称已存在");
				}
			}
		}
		CloudResourceEntity cloudResource = new CloudResourceEntity();
		cloudResource.setId(UUID.randomUUID().toString());
		cloudResource.setCreateTime(new Date());
		cloudResource.setName(msg.getName());
		cloudResource.setCpuCores(msg.getCpuCores());
		cloudResource.setMemory(msg.getMemory());
		cloudResource.setDatacenterId(msg.getDatacenterId());
		cloudResource.setType(msg.getType());
		cloudResource.setObjectId(msg.getObjectId());
		cloudResource.setFrom(ResourceSourceType.MANUAL.getName());
		
		//zhangdp保存云平台资源的配置信息
		ImportCloudResourceConfigMsg importMsg = new ImportCloudResourceConfigMsg();
		importMsg.setAliMigrationParam(msg.getAliMigrationParam());
		importMsg.setCloudResourceId(cloudResource.getId());
		importMsg.setClusterMigrationParam(msg.getClusterMigrationParam());
		importMsg.setHuaweiMigrationParam(msg.getHuaweiMigrationParam());
		importMsg.setType(msg.getType());
		importMsg.setDatacenterId(msg.getDatacenterId());
		String common = cloudResourceConfigService.importCluster(importMsg);



		Map<String, Object> expand = new HashMap<>();
		expand.put("nodeId", msg.getNodeId());
		cloudResource.setExpand(JsonUtil.toJSONObject(expand).toJSONString());
		cloudResourceDao.save(cloudResource);
		// TODO 新增模拟数据
		// mockRedis.set(cloudResource.getId(), MockType.CLOUD_RESOURCE,
		// cloudResource);

		return common;
	}

	@Override
	public CloudResourceEntity delete(String id) {
		CloudResourceEntity cloudResource = cloudResourceDao.getById(id);
		if (cloudResource != null) {
			List<AppEntity> apps = appDao.findByProperty("cloudResourceId", id);
			if (apps != null && apps.size() > 0) {
				throw new JeecgBootException("该云平台资源绑定应用【" + apps.get(0).getName() + "】,不能删除");
			}

			cloudResourceDao.deleteById(id);
			//删除配置
			cloudResourceConfigDao.deleteByCloudResourceId(id);
			// TODO 删除模拟数据
			// mockRedis.delete(id, MockType.CLOUD_RESOURCE);
		}
		return cloudResource;
	}

	@Override
	public CloudResourceItemVo detail(String id) {
		CloudResourceEntity entity = cloudResourceDao.getById(id);

		CloudResourceItemVo res = new CloudResourceItemVo();
		res.setId(entity.getId());
		res.setCreateTime(entity.getCreateTime());
		res.setDatacenterId(entity.getDatacenterId());
		res.setDatacenterName(datacenterDao.getById(entity.getDatacenterId()).getName());
		res.setName(entity.getName());
		res.setCpuCores(entity.getCpuCores());
		res.setMemory(entity.getMemory());
		res.setType(entity.getType());
		res.setTypeName(CloudResourceType.getByType(entity.getType()).getName());
		res.setObjectId(entity.getObjectId());

		String expand = entity.getExpand();
		JSONObject json = (JSONObject) JSONObject.parse(expand);
		if (!json.isEmpty()) {
			res.setNodeId(json.getString("nodeId"));
			IaasEntity iaas = iaasDao.getById(json.getString("nodeId"));
			if (iaas != null) {
				res.setNodeName(iaas.getName());
			}
		}

		return res;
	}

	@Override
	public void update(UpdateCloudResourceMsg msg) {
		CloudResourceEntity cloudResource = cloudResourceDao.getById(msg.getId());
		if (msg.getName() != null) {
			if (!cloudResource.getName().equals(msg.getName())) {
				List<CloudResourceEntity> cloudReources = cloudResourceDao.findByProperty("name", msg.getName());
				for (CloudResourceEntity cloudReource : cloudReources) {
					if (cloudReource != null && cloudReource.getName().equals(msg.getName())) {
						throw new ParamException("::资源名称已存在");
					}
				}
			}
			cloudResource.setName(msg.getName());
		}
		if (cloudResource.getFrom() == ResourceSourceType.SYNC.getName()) {
			throw new BaseException(CloudResourceErrorCode.CLOUD_SYNC_NOT_UPDATE);
		}
		if (msg.getDatacenterId() != null) {
			cloudResource.setDatacenterId(msg.getDatacenterId());
		}
		if (msg.getExpand() != null) {
			cloudResource.setExpand(msg.getExpand());
		}
		cloudResourceDao.update(cloudResource);

		// TODO 修改模拟数据
		// mockRedis.set(msg.getId(), MockType.CLOUD_RESOURCE, cloudResource);
	}

	@Override
	public PageResult<CloudResourceItemVo> page(@Valid PageCloudResourceMsg msg) {
		// PageResult<CloudResourceItemVo> page = cloudResourceDao.page(msg,
		// CloudResourceItemVo.class);
		PageResult<CloudResourceItemVo> page = cloudResourceDao.page(msg);
		if (page.getList() != null) {

			Map<String, String> nameMap = new HashMap<>();
			List<IaasEntity> nodes = iaasDao.findByProperty("type", DeviceType.SERVER.getNo());
			if (nodes != null) {
				for (IaasEntity node : nodes) {
					nameMap.put(node.getId(), node.getName());
				}
			}

			List<DatacenterEntity> datacenters = datacenterDao.findAll();
			if (datacenters != null) {
				for (DatacenterEntity datacenter : datacenters) {
					nameMap.put(datacenter.getId(), datacenter.getName());
				}
			}

			for (CloudResourceItemVo vo : page.getList()) {
				vo.setTypeName(CloudResourceType.getByType(vo.getType()).getName());
			}
		}
		return page;
	}

	@Override
	public String sync() {

		List<AuthorityVo> authorities = cloudResourceDao.listAuthority(AuthorityResourceClassification.CLOUD.getName());
		List<String> filterAuthorities = authorities.stream().filter(s -> !s.getEnabled()).map(s -> s.getId())
				.collect(Collectors.toList());

		List<String> allowAuthorities = authorities.stream().filter(s -> !filterAuthorities.contains(s.getId()))
				.map(s -> s.getId()).collect(Collectors.toList());

		String templateAll = "容器，虚拟机资源同步成功!";
		String templatePart = "%s同步成功！%s同步失败，请开启资源权限!";
		String templateNone = "容器，虚拟机资源同步失败，请开启资源权限!";
		String failContent = "";
		String successContent = "";

		for (String id : filterAuthorities) {
			CloudResourceType resourceType = CloudResourceType.getByEnName(id);
			failContent = failContent + resourceType.getName() + ",";
		}
		for (String id : allowAuthorities) {
			CloudResourceType resourceType = CloudResourceType.getByEnName(id);
			successContent = successContent + resourceType.getName() + ",";
		}

		// 获取对应云资源假数据
		List<MockCloudResourceVo> res = mockRedis.getByType(MockType.CLOUD_RESOURCE);
		if (res != null) {
			Map<String, String> cloudResourceMap = new HashMap<>();
			List<CloudResourceEntity> list = cloudResourceDao.findAll();
			for (CloudResourceEntity r : list) {
				cloudResourceMap.put(r.getId(), r.getId());
			}

			for (MockCloudResourceVo r : res) {
				r.setFrom(ResourceSourceType.SYNC.getName());
				String id = cloudResourceMap.get(r.getId());
				CloudResourceEntity cloud = new CloudResourceEntity();
				BeanUtils.copyProperties(r, cloud);
				cloud.setFrom(ResourceSourceType.SYNC.getName());
				cloud.setCreateTime(TimestampUtil.strToDate(r.getCreateTime(), "yyyy/MM/dd HH:mm:ss"));
				cloud.setObjectId(r.getObjectId());
				if (StringUtils.isBlank(id)) {
					cloudResourceDao.save(cloud);
				}
			}
		}

		if (filterAuthorities.size() == 2) {
			HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
			LogUtil.log(request, templateNone);
			throw new BaseException("000000", templateNone);
		}
		if (filterAuthorities.size() == 1) {
			String content = String.format(templatePart, successContent.substring(0, successContent.length() - 1),
					failContent.substring(0, failContent.length() - 1));
			return content;
		}
		return templateAll;
	}

	@Override
	public CloudResourceAnalysisVo analysis() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int count(Integer type) {
		return cloudResourceDao.count(type);
	}

	@Override
	public CloudResourceItemVo detail(String id, String userId, HttpServletRequest request, HttpServletResponse response) {
		// TODO Auto-generated method stub
		CloudResourceItemVo vo = detail(id);

		// 国密处理
		String sm2Cipher = request.getHeader(SM_TOKEN);
		if (StringUtils.isNotBlank(sm2Cipher)) {
			// 解密客户端密文
			String sm4Key = decryptSm2FromClient(sm2Cipher, userId);
			if (sm4Key != null) {
				String data = decryptSm4FromDb(vo.getObjectId(), userId);
				if (data != null && data.length() > 64) {
					int len = data.length();
					String sm3Key = data.substring(len - 64, len);
					String smData = data.substring(0, len - 64);
					// 加密数据保存到数据库
					String cipher = encryptToClient(sm3Key, sm4Key, smData, userId);
					vo.setObjectId(cipher);
					if (!sm3Verify(sm3Key, smData, userId)) {
						log.info("[SM][AssetServiceImpl][detailNode] SM3解密，数据完整性被破坏，存在被篡改风险{}【{}】", vo.getName(),
								vo.getId());
						vo.setObjectId(null);
						LogUtil.log(request, SysLogUrlOperate.errorSecurity);
					}
				} else {
					log.info("[SM][AssetServiceImpl][detailNode] SM4解密，数据完整性被破坏，存在被篡改风险{}【{}】", vo.getName(),
							vo.getId());
					vo.setObjectId(null);
					LogUtil.log(request, SysLogUrlOperate.errorSecurity);
				}
			}
		}

		return vo;
	}

	/**
	 * sm2解密
	 * 
	 * @param sm2Cipher
	 * @param userId
	 * @return
	 */
	private String decryptSm2FromClient(String sm2Cipher, String userId) {
		String smKey = null;
		try {
			smKey = smService.decryptSm2(sm2Cipher, userId);
		} catch (Exception e) {
			log.error("[SM][AssetServiceImpl][decryptSm2FromClient] 解密失败");
		}
		return smKey;
	}
	
	/**
	 * 一次一密
	 * 
	 * @param key
	 * @param sm2Cipher
	 * @param cipher
	 * @param userId
	 * @return
	 */
	private String decryptFromClient(String sm4Key, String cipher, String userId) {
		String data = "";
		try {
			data = smService.decryptClientSm4(sm4Key, cipher, userId);
		} catch (Exception e) {
			log.error("[SM][AssetServiceImpl][encryptFromClient] 解密失败");
		}
		return data;
	}

	/**
	 * 一次一密
	 * 
	 * @param sm4Key
	 * @param data
	 * @param userId
	 * @return
	 */
	private String encryptToClient(String sm3Key, String sm4Key, String data, String userId) {
		String cipher = "";
		try {
			String newData = data + sm3Key;
			cipher = smService.encryptClientSm4(sm4Key, newData, userId);
		} catch (Exception e) {
			log.error("[SM][AssetServiceImpl][encryptToClient] 加密失败");
		}
		return cipher;
	}

	/**
	 * sm3 验证
	 * 
	 * @param key
	 * @param data
	 * @param userId
	 * @return
	 */
	private boolean sm3Verify(String key, String data, String userId) {
		boolean res = false;
		try {
			res = smService.verifySm3(data, key);
		} catch (Exception e) {
			log.error("[SM][AssetServiceImpl][sm3Verify] SM3 加密异常：{}", e.getMessage());
		}
		return res;
	}
	
	/**
	 * 一次一密
	 * 
	 * @param cipher
	 * @param userId
	 * @return
	 */
	private String decryptSm4FromDb(String cipher, String userId) {
		log.info("[AssetServiceImpl][encryptSm4ToClient] 获取数据库中SM4密文：{}", cipher);
		String data = null;
		try {
			data = smService.decryptServerSm4(cipher, userId);
			log.info("[SM][AssetServiceImpl][encryptSm4ToClient] SM4解密数据库密文得到明文：{}", data);
		} catch (Exception e) {
			log.error("[AssetServiceImpl][encryptSm4ToClient] 国密加解密异常：{}", e.getMessage());
		}
		return data;
	}

	@Override
	public String create(@Valid CreateCloudResourceMsg msg, String userId, HttpServletRequest request,
			HttpServletResponse response) {
		// TODO Auto-generated method stub
		// 国密处理
		String sm2Cipher = request.getHeader(SM_TOKEN);
		if (StringUtils.isNotBlank(sm2Cipher) && StringUtils.isNotBlank(msg.getObjectId())) {
			// 解密客户端密文
			String sm4Key = decryptSm2FromClient(sm2Cipher, userId);
			if (sm4Key != null) {
				String data = decryptFromClient(sm4Key, msg.getObjectId(), userId);
				if (data != null && data.length() > 64) {
					int len = data.length();
					String SM3Key = data.substring(len - 64, len);
					String smData = data.substring(0, len - 64);
					// 加密数据保存到数据库
					msg.setObjectId(encryptSm4SaveDb(smData, userId));
					if (!sm3Verify(SM3Key, smData, userId)) {
						log.info("[SM][CloudResourceServiceImpl][create] 数据完整性被破坏，存在被篡改风险【{}】", msg.getName());
						throw new ParamException(null, "完整性被破坏, 请注意数据安全");
					}
				} else {
					throw new ParamException(null, "完整性被破坏, 请注意数据安全");
				}
			}

		} else {
			// log.error("[SM][AssetServiceImpl][createNode] 客户端秘钥解密失败");
			// throw new ParamException(null, "完整性被破坏, 请注意数据安全");
		}

		return create(msg);
	}

	@Override
	public void update(UpdateCloudResourceMsg msg, String userId, HttpServletRequest request,
			HttpServletResponse response) {
		// TODO Auto-generated method stub
		update(msg);
	}
	
	/**
	 * 一次一密
	 * 
	 * @param data
	 * @param userId
	 * @return
	 */
	private String encryptSm4SaveDb(String data, String userId) {
		String cipher = null;
		try {
			String SM3Key = smService.encryptSm3(data, userId);
			String newData = data + SM3Key;
			cipher = smService.encryptServerSm4(newData, userId);

			log.info("[SM][AssetServiceImpl][encryptSm4SaveDb] SM4加密存储到数据库中的密文： {}", cipher);

		} catch (Exception e) {
			log.error("[SM][AssetServiceImpl][encryptSm4SaveDb] 国密加解密异常：{}", e.getMessage());
		}
		return cipher;
	}
	
//	@Override
//	@Transactional
//	public void updateMigrationResult(String appId, String destDatacenterId, String destNodeId, String schedulerModel) {
//
//		AppEntity app = appDao.getById(appId);
//		CloudResourceItemVo critem = cloudResourceService.detail(app.getCloudResourceId());
//		String sourceNodeId = critem.getNodeId();
//		UpdateCloudResourceMsg msg = new UpdateCloudResourceMsg();
//		msg.setId(app.getCloudResourceId());
//		msg.setDatacenterId(destDatacenterId);
//		Map<String, Object> expand = new HashMap<>();
//		expand.put("nodeId", destNodeId);
//		msg.setExpand(JsonUtil.toJSONObject(expand).toJSONString());
//		cloudResourceService.update(msg);
//
//		ModelType modelType = ModelType.get(SchedulerModelDict.get(schedulerModel).name());
//		SchedulerImagineEntity entity = schedulerImagineDao.get(modelType, appId, null,
//				null, SchedulerImagineEntity.class);
//
//		String temperatureStr = statisticsCheatService.latestSample(MonitorMeter.DATACENTER_TEMPERATURE.getMeter(),
//				destDatacenterId);
//		String distributionBoxVoltageStr = statisticsCheatService
//				.latestSample(MonitorMeter.DATACENTER_LOAD.getMeter(), destDatacenterId);
//		String datacenterEnergyStr = statisticsCheatService.latestSample(MonitorMeter.DATACENTER_POWER.getMeter(),
//				destDatacenterId);
//		String businessRespondStr = statisticsCheatService.latestSample(MonitorMeter.APP_RESPOND_TIME.getMeter(),
//				app.getId());
//
//		Double temperature = Double.parseDouble(temperatureStr);
//		Double distributionBoxVoltage = Double.parseDouble(distributionBoxVoltageStr);
//		Double datacenterEnergy = Double.parseDouble(datacenterEnergyStr);
//		Double businessRespond = Double.parseDouble(businessRespondStr);
//
//		BigDecimal temperatureBeforeb = new BigDecimal(temperature);
//		temperature = temperatureBeforeb.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
//		BigDecimal distributionBoxVoltageBeforeb = new BigDecimal(distributionBoxVoltage);
//		distributionBoxVoltage = distributionBoxVoltageBeforeb.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
//		BigDecimal datacenterEnergyBeforeb = new BigDecimal(datacenterEnergy);
//		datacenterEnergy = datacenterEnergyBeforeb.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
//		BigDecimal businessRespondBeforeb = new BigDecimal(businessRespond);
//		businessRespond = businessRespondBeforeb.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
//
//		if (entity != null) {
//			schedulerImagineDao.updateAfter(entity, temperature, distributionBoxVoltage, datacenterEnergy,
//					businessRespond);
//		} else {
//			SchedulerImagineEntity schedulerImagineEntity = new SchedulerImagineEntity();
//			schedulerImagineEntity.setId(UUID.randomUUID().toString());
//			schedulerImagineEntity.setAppId(app.getId());
//			schedulerImagineEntity.setSourceNodeId(sourceNodeId);
//			schedulerImagineEntity.setDestNodeId(destNodeId);
//			schedulerImagineEntity.setModelType(modelType.name());
//			schedulerImagineEntity.setUpdateTime(new Date());
//			schedulerImagineEntity.setBusinessRespond(businessRespond);
//			schedulerImagineEntity.setDistributionBoxVoltage(distributionBoxVoltage);
//			schedulerImagineEntity.setTemperature(temperature);
//			schedulerImagineEntity.setDatacenterEnergy(datacenterEnergy);
//			schedulerImagineDao.save(schedulerImagineEntity);
//		}
//
//	}

}
